package org.example.backend.Base.Config;

import cn.hutool.http.ContentType;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.example.backend.Base.Filter.JWTFilter;
import org.example.backend.Base.Response.JsonResult;
import org.example.backend.Base.Response.StatusCode;
import org.example.backend.Module.System.User.Service.Impl.MyUserDetailsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.servlet.ServletOutputStream;

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    private final ObjectMapper objectMapper = new ObjectMapper();

    @Bean
    @Autowired
    public SecurityFilterChain securityFilterChain(HttpSecurity http, JWTFilter jwtFilter) throws Exception {
        return http
                .csrf(AbstractHttpConfigurer::disable)
                .formLogin().disable()
                .sessionManagement(sessionManagement -> sessionManagement.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                .authorizeRequests(auth -> auth
                        .antMatchers("/v1/user/login").permitAll()
                        .antMatchers("/v2/api-docs").permitAll()
                        .antMatchers("/doc.html").permitAll()
                        .antMatchers("/swagger-resources").permitAll()
                        .antMatchers("/webjars/**").permitAll()
                        .anyRequest().authenticated())
                .exceptionHandling(exception -> {
                    exception.authenticationEntryPoint((request, response, authException) -> {
                        response.setContentType(ContentType.JSON.toString());

                        ServletOutputStream outputStream = response.getOutputStream();

                        objectMapper.writeValue(outputStream, new JsonResult(StatusCode.NOT_LOGIN));
                    });
                    exception.accessDeniedHandler((request, response, accessDeniedException) -> {
                        response.setContentType(ContentType.JSON.toString());

                        ServletOutputStream outputStream = response.getOutputStream();

                        objectMapper.writeValue(outputStream, new JsonResult(StatusCode.AUTHORITY_ERROR));
                    });
                })
                .addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class)
                .build();
    }

    @Bean
    @Autowired
    public AuthenticationManager authenticationManager(MyUserDetailsService service, PasswordEncoder passwordEncoder) {
        DaoAuthenticationProvider provider = new DaoAuthenticationProvider();

        provider.setUserDetailsService(service);
        provider.setPasswordEncoder(passwordEncoder);

        return new ProviderManager(provider);
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new PasswordEncoder() {
            @Override
            public String encode(CharSequence rawPassword) {
                return rawPassword.toString();
            }

            @Override
            public boolean matches(CharSequence rawPassword, String encodedPassword) {
                return rawPassword.toString().equals(encodedPassword);
            }
        };
    }
}
